﻿using System.CodeDom;
using System.Collections.Generic;
using CodeDomHelper.Types;
using System;

namespace CodeDomHelper.CodeDom
{


    public static partial class Helper
    {

        #region Comment

        public static void Comment(this CodeStatementCollection code, string comment)
        {
            code.Add(new CodeCommentStatement(comment));
        }

        public static CodeCommentStatement Comment(string comment)
        {
            return new CodeCommentStatement(comment);
        }

        public static void CommentDoc(this CodeStatementCollection code, string comment)
        {
            code.Add(new CodeCommentStatement(comment, true));
        }

        public static CodeCommentStatement CommentDoc(string comment)
        {
            return new CodeCommentStatement(comment, true);
        }

        #endregion

        #region Operators

        public static CodeBinaryOperatorExpression Add(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Add, right);
        }
        public static CodeBinaryOperatorExpression Assign(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Assign, right);
        }
        public static CodeBinaryOperatorExpression BitwiseAnd(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BitwiseAnd, right);
        }
        public static CodeBinaryOperatorExpression BitwiseOr(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BitwiseOr, right);
        }
        public static CodeBinaryOperatorExpression BooleanAnd(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanAnd, right);
        }
        public static CodeBinaryOperatorExpression BooleanOr(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanOr, right);
        }
        public static CodeBinaryOperatorExpression Divide(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Divide, right);
        }
        public static CodeBinaryOperatorExpression GreaterThan(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.GreaterThan, right);
        }
        public static CodeBinaryOperatorExpression GreaterThanOrEqual(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.GreaterThanOrEqual, right);
        }
        public static CodeBinaryOperatorExpression IdentityEquality(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityEquality, right);
        }
        public static CodeBinaryOperatorExpression LessThan(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.LessThan, right);
        }
        public static CodeBinaryOperatorExpression IdentityInequality(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityInequality, right);
        }
        public static CodeBinaryOperatorExpression Modulus(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Modulus, right);
        }
        public static CodeBinaryOperatorExpression LessThanOrEqual(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.LessThanOrEqual, right);
        }
        public static CodeBinaryOperatorExpression ValueEquality(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.ValueEquality, right);
        }
        public static CodeBinaryOperatorExpression Subtract(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Subtract, right);
        }
        public static CodeBinaryOperatorExpression Multiply(CodeExpression left, CodeExpression right)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Multiply, right);
        }
        public static CodeBinaryOperatorExpression IsNull(CodeExpression left)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
        }
        public static CodeBinaryOperatorExpression IsNotNull(CodeExpression left)
        {
            return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
        }

        #endregion

        #region Add

        /// <summary>
        /// Increments left from 1.
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="left">The left.</param>
        /// <returns></returns>
        public static void Increment(this CodeStatementCollection code, VariableExp left)
        {
            var i = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Add, new PrimitiveExp(1));
            Set(code, left, i);
        }

        /// <summary>
        /// Increments left from value
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="value">The value.</param>
        /// <param name="left">The left.</param>
        /// <returns></returns>
        public static void Increment(this CodeStatementCollection code, VariableExp left, long value)
        {
            var i = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Add, new PrimitiveExp(value));
            Set(code, left, i);
        }

        /// <summary>
        /// Increments left from value
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="value">The value.</param>
        /// <param name="left">The left.</param>
        /// <returns></returns>
        public static void Increment(this CodeStatementCollection code, VariableExp left, VariableExp right)
        {
            var i = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.Add, right);
            Set(code, left, i);
        }

        #endregion

        #region Delegate / Event

        public static CodeDelegateCreateExpression CreateDelegate(this CodeStatementCollection code, TypeExp type, CodeExpression targetObject, string delegateName)
        {

            CodeDelegateCreateExpression delegate1
                = new CodeDelegateCreateExpression
                (
                    type,
                    targetObject,
                    delegateName
                );

            return delegate1;
        }

        public static void AttachEvent(this CodeStatementCollection code, CodeExpression targetObject, string eventName, CodeDelegateCreateExpression listener)
        {

            CodeAttachEventStatement attachStatement1
                = new CodeAttachEventStatement(
                    targetObject,
                    eventName,
                    listener
                );

            code.Add(attachStatement1);

        }

        public static void DetachEvent(this CodeStatementCollection code, CodeExpression targetObject, string eventName, CodeDelegateCreateExpression listener)
        {

            CodeRemoveEventStatement detachStatement1
                = new CodeRemoveEventStatement(
                    targetObject,
                    eventName,
                    listener
                );

            code.Add(detachStatement1);

        }

        #endregion

        #region Throw

        public static void Throw(this CodeStatementCollection code, TypeExp type, string message)
        {
            var i = new CodeThrowExceptionStatement(new CodeObjectCreateExpression(type, new CodePrimitiveExpression(message)));
            code.Add(i);
        }

        public static void Throw(this CodeStatementCollection code, CodeExpression toThrow)
        {
            code.Add(toThrow);
        }

        #endregion

        #region Try

        public static void Catch(this CodeCatchClauseCollection code, string localName, TypeExp type)
        {
            CodeCatchClause Catch = new CodeCatchClause(localName, type);
            code.Add(Catch);
        }

        public static void Catch(this CodeCatchClauseCollection code, CodeCatchClause Catch)
        {
            code.Add(Catch);
        }

        public static CodeTryCatchFinallyStatement Try(this CodeStatementCollection code)
        {
            var i = new CodeTryCatchFinallyStatement(new CodeStatement[0], new CodeCatchClause[0], new CodeStatement[0]);
            code.Add(i);
            return i;
        }

        public static CodeTryCatchFinallyStatement Try(this CodeStatementCollection code, CodeStatement[] tryStatements)
        {
            var i = new CodeTryCatchFinallyStatement(tryStatements, new CodeCatchClause[0], new CodeStatement[0]);
            code.Add(i);
            return i;
        }

        public static CodeTryCatchFinallyStatement Try(this CodeStatementCollection code, CodeStatement[] tryStatements, CodeCatchClause[] catchClauses)
        {
            var i = new CodeTryCatchFinallyStatement(tryStatements, catchClauses, new CodeStatement[0]);
            code.Add(i);
            return i;
        }

        public static CodeTryCatchFinallyStatement Try(this CodeStatementCollection code, CodeStatement[] tryStatements, CodeCatchClause[] catchClauses, CodeStatement[] finallyStatements)
        {
            var i = new CodeTryCatchFinallyStatement(tryStatements, catchClauses, finallyStatements);
            code.Add(i);
            return i;
        }

        #endregion

        #region conditions

        public static CodeConditionStatement If(this CodeStatementCollection code, CodeExpression condition)
        {
            var IF = new CodeConditionStatement(condition, new CodeStatement[0], new CodeStatement[0]);
            code.Add(IF);
            return IF;
        }

        public static CodeConditionStatement If(this CodeStatementCollection code, CodeExpression condition, CodeStatement[] trueStatements)
        {
            var IF = new CodeConditionStatement(condition, trueStatements);
            code.Add(IF);
            return IF;
        }

        public static CodeConditionStatement If(this CodeStatementCollection code, CodeExpression condition, CodeStatement trueStatement)
        {
            var IF = new CodeConditionStatement(condition, new CodeStatement[1] { trueStatement });
            code.Add(IF);
            return IF;
        }

        public static CodeConditionStatement If(this CodeStatementCollection code, CodeExpression condition, CodeStatement trueStatement, CodeStatement falseStatement)
        {
            var IF = new CodeConditionStatement(condition, new CodeStatement[1] { trueStatement }, new CodeStatement[1] { falseStatement });
            code.Add(IF);
            return IF;
        }

        public static CodeConditionStatement If(this CodeStatementCollection code, CodeExpression condition, CodeStatement[] trueStatements, CodeStatement[] falseStatements)
        {
            var IF = new CodeConditionStatement(condition, trueStatements, falseStatements);
            code.Add(IF);
            return IF;
        }

        #endregion return

        #region Return

        public static void Return(this CodeStatementCollection code, CodeExpression expression)
        {
            code.Add(new CodeMethodReturnStatement(expression));
        }

        public static CodeMethodReturnStatement ReturnNull() { return new CodeMethodReturnStatement(Null); }


        #endregion

        #region Assign

        public static void SetWithCastIfNeed(this CodeStatementCollection code, IType left, IType right)
        {
            if (left.Type == right.Type)
                Set(code, left as CodeExpression, right as CodeExpression);
            else
                SetWithCast(code, left as CodeExpression, right as CodeExpression, left.Type);
        }
        public static CodeAssignStatement SetWithCastIfNeed(IType left, IType right)
        {
            if (left.Type == right.Type)
                return Set(left as CodeExpression, right as CodeExpression);

            return SetWithCast(left as CodeExpression, right as CodeExpression, left.Type);
        }
        public static void Set(this CodeStatementCollection code, System.CodeDom.CodeExpression left, System.CodeDom.CodeExpression right)
        {
            code.Add(new CodeAssignStatement(left, right));
        }
        public static CodeAssignStatement Set(System.CodeDom.CodeExpression left, System.CodeDom.CodeExpression right)
        {
            return new CodeAssignStatement(left, right);
        }
        public static void SetWithCast(this CodeStatementCollection code, System.CodeDom.CodeExpression left, System.CodeDom.CodeExpression right, TypeExp type)
        {
            code.Add(new CodeAssignStatement(left, new CodeCastExpression(type, right)));
        }
        public static CodeAssignStatement SetWithCast(System.CodeDom.CodeExpression left, System.CodeDom.CodeExpression right, TypeExp type)
        {
            return new CodeAssignStatement(left, new CodeCastExpression(type, right));
        }

        #endregion

        #region Declare

        public static VariableExp Declare(this CodeStatementCollection code, VariableExp newLine)
        {
            code.Add(new CodeVariableDeclarationStatement(newLine.Type, newLine.VariableName));
            return newLine;
        }

        public static VariableExp Declare(this CodeStatementCollection code, VariableExp newLine, CodeExpression initExpression)
        {
            code.Add(new CodeVariableDeclarationStatement(newLine.Type, newLine.VariableName, initExpression));
            return newLine;
        }

        public static VariableExp Declare(this CodeStatementCollection code, string variableName, TypeExpression type)
        {
            return Declare(code, new VariableExp(variableName, type));
        }

        public static VariableExp Declare(this CodeStatementCollection code, string variableName, TypeExp type)
        {
            return Declare(code, new VariableExp(variableName, type));
        }

        public static VariableExp Declare(this CodeStatementCollection code, string variableName, TypeExp type, CodeExpression initExpression)
        {
            return Declare(code, new VariableExp(variableName, type), initExpression);
        }

        public static VariableExp Declare(this CodeStatementCollection code, string variableName, TypeExpression type, CodeExpression initExpression)
        {
            var t = new VariableExp(variableName, type);
            var i = new CodeVariableDeclarationStatement(t.Type, t.VariableName, initExpression);
            code.Add(i);
            return t;
        }

        #endregion

        #region Iteration

        public static CodeIterationStatement ForEach(this CodeStatementCollection code, TypeExpression type, VariableExp source, out VariableExp current, string nameCurrent, string basenNameEnumerator)
        {

            current = new VariableExp(nameCurrent, type);

            var t1 = TypeExpression.Analyse(typeof(IEnumerator<>).AssemblyQualifiedName);
            t1.Add(type);

            //var t1 = source.Type.TypeExpression.Translate(typeof(IEnumerator<>));
            //if (t1.SubType == null)
            //    t1.Add(type);

            var t = new TypeExp(t1);


            // declare la variable enumerator a l'exterieur de la boucle
            var enumerator = code.Declare
            (
                basenNameEnumerator + source.VariableName, t,
                source.Method("GetEnumerator", t).Call()
            );

            code.EmptyLine();

            CodeIterationStatement f
                = new CodeIterationStatement
                (
                    new CodeSnippetStatement(),
                    enumerator.Method("MoveNext", source.Type).Call(),
                    new CodeSnippetStatement()
                );

            f.Statements.Declare(current, enumerator.Property("Current", type));

            code.Add(f);

            return f;
        }

        #endregion


        public static CodeTypeOfExpression TypeOf(this CodeTypeReference type) { return new CodeTypeOfExpression(type); }
        public static CodeTypeOfExpression TypeOf(this Type type) { return new CodeTypeOfExpression(type); }
        public static CodeTypeOfExpression TypeOf(this string type) { return new CodeTypeOfExpression(type); }

        public static CodeExpression Index(this VariableExp array, params CodeExpression[] index)
        {

            CodeArrayIndexerExpression ci1
                = new CodeArrayIndexerExpression(array, index);

            return ci1;
        }

        public static CodeExpression Null { get { return TypeExp.Null; } }
        public static CodeExpression Primitive(object o) { return new CodePrimitiveExpression(o); }
        public static CodeExpression Zero { get { return new CodePrimitiveExpression(0); } }
        public static CodeExpression True { get { return new CodePrimitiveExpression(true); } }
        public static CodeExpression False { get { return new CodePrimitiveExpression(false); } }


        public static TypeExp Integer { get { return new TypeExp(typeof(int)); } }
        public static TypeExp String { get { return new TypeExp(typeof(string)); } }
        public static TypeExp Decimal { get { return new TypeExp(typeof(decimal)); } }
        public static TypeExp Float { get { return new TypeExp(typeof(float)); } }        
        public static TypeExp Short { get { return new TypeExp(typeof(short)); } }
        public static TypeExp Long { get { return new TypeExp(typeof(long)); } }
        public static TypeExp Guid { get { return new TypeExp(typeof(Guid)); } }
        public static TypeExp Bool { get { return new TypeExp(typeof(bool)); } }


        public static CastExp Cast(System.CodeDom.CodeExpression obj, TypeExp type)
        {
            return new CastExp(type, obj);
        }

        public static CodeThisReferenceExpression This(this CodeExpression code)
        {
            return new CodeThisReferenceExpression();
        }

        public static CodeThisReferenceExpression This()
        {
            return new CodeThisReferenceExpression();
        }

        public static void EmptyLine(this CodeStatementCollection code)
        {
            //code.Add(new System.CodeDom.CodeSnippetExpression(string.Empty));
        }


        
    }


}

